Carga inicial del dataframe de tweets COVID-19
library(tidyverse)
library(mongolite)
library(ggplot2)
library(plotly)
Registered S3 method overwritten by 'data.table':
method from
print.data.table
Registered S3 method overwritten by 'htmlwidgets':
method from
print.htmlwidget tools:rstudio
Attaching package: ‘plotly’
The following object is masked from ‘package:ggplot2’:
last_plot
The following object is masked from ‘package:stats’:
filter
The following object is masked from ‘package:graphics’:
layout
library(hrbrthemes)
NOTE: Either Arial Narrow or Roboto Condensed fonts are required to use these themes.
Please use hrbrthemes::import_roboto_condensed() to install Roboto Condensed and
if Arial Narrow is not on your system, please see https://bit.ly/arialnarrow
library(dplyr)
library(here)
tweets_mongo_covid19 <- mongo(
collection = "tweets_mongo_covid19",
db = "DMUBA"
)
hitos_df <- read.csv(
here("resources" , "hitos_discretizado.csv")
)
Mongo Query
df_tweets = tweets_mongo_covid19$aggregate(
'[
{
"$match": {}
},
{
"$project": {
"status_id": 1,
"user_id": 1,
"screen_name": 1,
"verified": 1,
"location": 1,
"source": 1,
"favorite_count": 1,
"retweet_count": 1,
"created_at": {
"$dateToString": { "date": "$created_at"}
},
"retweet_status_id": 1,
"retweet_user_id": 1,
"retweet_screen_name": 1,
"retweet_verified": 1,
"retweet_location": 1,
"retweet_source": 1,
"retweet_favorite_count": 1,
"retweet_retweet_count": 1,
"retweet_created_at": {
"$cond": {
"if": {
"$eq" : ["$retweet_created_at", {}]
},
"then": null,
"else": {
"$dateToString": {"date": "$retweet_created_at"}
}
}
},
"quoted_status_id": 1,
"quoted_user_id": 1,
"quoted_screen_name": 1,
"quoted_verified": 1,
"quoted_location": 1,
"quoted_source": 1,
"quoted_favorite_count": 1,
"quoted_retweet_count": 1,
"quoted_created_at": {
"$cond": {
"if": {
"$eq" : ["$quoted_created_at", {}]
},
"then": null,
"else": {
"$dateToString": {"date": "$quoted_created_at"}
}
}
}
}
}
]'
)
Unificamos tweets originales, retweets y quotes bajo atributos comunes. ( ‘status_id’, ‘user_id’, ‘screen_name’, ‘verified’, ‘location’, ‘source’, ‘favorite_count’, ‘retweet_count’, ‘created_at’ )
# Original Tweets
original_tweets_header <- c(
'status_id',
'user_id',
'screen_name',
'verified',
'location',
'source',
'favorite_count',
'retweet_count',
'created_at'
)
original_tweets = df_tweets[,original_tweets_header]
# Retweets
retweeted_tweets_header <- c(
'retweet_status_id',
'retweet_user_id',
'retweet_screen_name',
'retweet_verified',
'retweet_location',
'retweet_source',
'retweet_favorite_count',
'retweet_retweet_count',
'retweet_created_at'
)
retweeted_tweets = df_tweets[,retweeted_tweets_header]
names(retweeted_tweets)[names(retweeted_tweets) == 'retweet_status_id'] <- 'status_id'
names(retweeted_tweets)[names(retweeted_tweets) == 'retweet_user_id'] <- 'user_id'
names(retweeted_tweets)[names(retweeted_tweets) == 'retweet_screen_name'] <- 'screen_name'
names(retweeted_tweets)[names(retweeted_tweets) == 'retweet_verified'] <- 'verified'
names(retweeted_tweets)[names(retweeted_tweets) == 'retweet_location'] <- 'location'
names(retweeted_tweets)[names(retweeted_tweets) == 'retweet_source'] <- 'source'
names(retweeted_tweets)[names(retweeted_tweets) == 'retweet_favorite_count'] <- 'favorite_count'
names(retweeted_tweets)[names(retweeted_tweets) == 'retweet_retweet_count'] <- 'retweet_count'
names(retweeted_tweets)[names(retweeted_tweets) == 'retweet_created_at'] <- 'created_at'
# Quotes
quoted_tweets_header <- c(
'quoted_status_id',
'quoted_user_id',
'quoted_screen_name',
'quoted_verified',
'quoted_location',
'quoted_source',
'quoted_favorite_count',
'quoted_retweet_count',
'quoted_created_at'
)
quoted_tweets = df_tweets[,quoted_tweets_header]
names(quoted_tweets)[names(quoted_tweets) == 'quoted_status_id'] <- 'status_id'
names(quoted_tweets)[names(quoted_tweets) == 'quoted_user_id'] <- 'user_id'
names(quoted_tweets)[names(quoted_tweets) == 'quoted_screen_name'] <- 'screen_name'
names(quoted_tweets)[names(quoted_tweets) == 'quoted_verified'] <- 'verified'
names(quoted_tweets)[names(quoted_tweets) == 'quoted_location'] <- 'location'
names(quoted_tweets)[names(quoted_tweets) == 'quoted_source'] <- 'source'
names(quoted_tweets)[names(quoted_tweets) == 'quoted_favorite_count'] <- 'favorite_count'
names(quoted_tweets)[names(quoted_tweets) == 'quoted_retweet_count'] <- 'retweet_count'
names(quoted_tweets)[names(quoted_tweets) == 'quoted_created_at'] <- 'created_at'
Combinamos los tweets y aplicamos formateo a los valores de fecha
combined_tweets = rbind(original_tweets, retweeted_tweets, quoted_tweets)
combined_tweets['created_at_R_date'] = as.POSIXct(
combined_tweets$created_at,
format="%Y-%m-%dT",
tz="UTC"
)
combined_tweets['created_at'] = as.POSIXct(
combined_tweets$created_at,
format="%Y-%m-%dT%H:%M:%S",
tz="UTC"
)
# Limpiamos duplicados
combined_tweets = combined_tweets[!duplicated(combined_tweets),]
combined_tweets[!is.na(combined_tweets['created_at_R_date']),]
NA
Transformamos nuestro dataset de hitos
hitos_df['Fecha'] = as.Date(hitos_df$Fecha, "%d/%m/%Y")
hitos_df['Primera'] <- ifelse(hitos_df['Primera'] == "S", 1, 0)
tweet_counts_by_date = as.data.frame(
combined_tweets %>%
group_by(created_at_R_date) %>%
count(created_at_R_date)
)
names(tweet_counts_by_date)[1] = 'date'
names(tweet_counts_by_date)[2] = 'count'
summary(hitos_df)
Fecha Evento Primera.Primera
Min. :2020-04-25 Prisiones domiciliarias : 7 Min. :0.0000000
1st Qu.:2020-05-01 AMBA Flexibilización 500m : 2 1st Qu.:1.0000000
Median :2020-05-06 CuidAR : 2 Median :1.0000000
Mean :2020-05-06 Deuda: Bonistas Rechazan Oferta: 2 Mean :0.8275862
3rd Qu.:2020-05-11 Expaña Flexibilización : 2 3rd Qu.:1.0000000
Max. :2020-05-15 Extensión Cuarentena : 2 Max. :1.0000000
(Other) :41
# From summary
hitos_min_date=as.POSIXct("2020-04-25")
hitos_max_date=as.POSIXct("2020-05-15")
tweet_counts_by_date_range = as.data.frame(
tweet_counts_by_date %>%
filter(date >= hitos_min_date & date <= hitos_max_date)
)
summary(tweet_counts_by_date_range)
date count
Min. :2020-04-26 00:00:00 Min. : 39.0
1st Qu.:2020-04-30 18:00:00 1st Qu.: 403.5
Median :2020-05-05 12:00:00 Median : 2248.0
Mean :2020-05-05 12:00:00 Mean : 2673.7
3rd Qu.:2020-05-10 06:00:00 3rd Qu.: 3063.0
Max. :2020-05-15 00:00:00 Max. :16626.0
Graficamos la cantidad de tweets por día
# Usual area chart
p <- tweet_counts_by_date_range %>%
ggplot( aes(x=date, y=count)) +
geom_area(fill="#69b3a2", alpha=0.5) +
geom_line(color="#69b3a2") +
ylab("tweets by news events dates") +
theme_ipsum()
# Turn it interactive with ggplotly
p <- ggplotly(p)
p
Exploramos el uso de hashtags
Ya teniendo una primera impresión de la evolución de los tweets en base sus fechas, exploramos el uso de hashtags
expanded_hashtags = tweets_mongo_covid19$aggregate(
'[
{
"$unwind": "$hashtags"
},
{
"$project": {
"status_id": 1,
"verified": 1,
"location": 1,
"source": 1,
"created_at": {
"$dateToString": { "date": "$created_at"}
},
"favorite_count": 1,
"retweet_count": 1,
"retweet_status_id": 1,
"retweet_verified": 1,
"retweet_location":1,
"retweet_source": 1,
"retweet_created_at": {
"$cond": {
"if": {
"$eq" : ["$retweet_created_at", {}]
},
"then": null,
"else": {
"$dateToString": {"date": "$retweet_created_at"}
}
}
},
"retweet_favorite_count": 1,
"retweet_retweet_count": 1,
"quoted_status_id": 1,
"quoted_verified": 1,
"quoted_location": 1,
"quoted_source": 1,
"quoted_created_at": {
"$cond": {
"if": {
"$eq" : ["$quoted_created_at", {}]
},
"then": null,
"else": {
"$dateToString": {"date": "$quoted_created_at"}
}
}
},
"quoted_favorite_count": 1,
"quoted_retweet_count": 1,
"hashtags": 1
}
}
]'
)
original_tweet_headers <- c(
'status_id',
'verified',
'location',
'source',
'created_at',
'favorite_count',
'retweet_count',
'hashtags'
)
original_tweet_hashtags = expanded_hashtags[,original_tweet_headers]
# Retweets
retweeted_tweet_headers <- c(
'retweet_status_id',
'retweet_verified',
'retweet_location',
'retweet_source',
'retweet_created_at',
'retweet_favorite_count',
'retweet_retweet_count',
'hashtags'
)
retweeted_tweet_hashtags = expanded_hashtags[,retweeted_tweet_headers]
names(retweeted_tweet_hashtags)[names(retweeted_tweet_hashtags) == 'retweet_status_id'] <- 'status_id'
names(retweeted_tweet_hashtags)[names(retweeted_tweet_hashtags) == 'retweet_verified'] <- 'verified'
names(retweeted_tweet_hashtags)[names(retweeted_tweet_hashtags) == 'retweet_location'] <- 'location'
names(retweeted_tweet_hashtags)[names(retweeted_tweet_hashtags) == 'retweet_source'] <- 'source'
names(retweeted_tweet_hashtags)[names(retweeted_tweet_hashtags) == 'retweet_created_at'] <- 'created_at'
names(retweeted_tweet_hashtags)[names(retweeted_tweet_hashtags) == 'retweet_favorite_count'] <- 'favorite_count'
names(retweeted_tweet_hashtags)[names(retweeted_tweet_hashtags) == 'retweet_retweet_count'] <- 'retweet_count'
# Quotes
quoted_tweet_headers <- c(
'quoted_status_id',
'quoted_verified',
'quoted_location',
'quoted_source',
'quoted_created_at',
'quoted_favorite_count',
'quoted_retweet_count',
'hashtags'
)
quoted_tweet_hashtags = expanded_hashtags[,quoted_tweet_headers]
names(quoted_tweet_hashtags)[names(quoted_tweet_hashtags) == 'quoted_status_id'] <- 'status_id'
names(quoted_tweet_hashtags)[names(quoted_tweet_hashtags) == 'quoted_verified'] <- 'verified'
names(quoted_tweet_hashtags)[names(quoted_tweet_hashtags) == 'quoted_location'] <- 'location'
names(quoted_tweet_hashtags)[names(quoted_tweet_hashtags) == 'quoted_source'] <- 'source'
names(quoted_tweet_hashtags)[names(quoted_tweet_hashtags) == 'quoted_created_at'] <- 'created_at'
names(quoted_tweet_hashtags)[names(quoted_tweet_hashtags) == 'quoted_favorite_count'] <- 'favorite_count'
names(quoted_tweet_hashtags)[names(quoted_tweet_hashtags) == 'quoted_retweet_count'] <- 'retweet_count'
combined_hashtags = rbind(original_tweet_hashtags, retweeted_tweet_hashtags, quoted_tweet_hashtags)
combined_hashtags['created_at_R_date'] = as.POSIXct(
combined_hashtags$created_at,
format="%Y-%m-%dT",
tz="UTC"
)
combined_hashtags['created_at'] = as.POSIXct(
combined_hashtags$created_at,
format="%Y-%m-%dT%H:%M:%S",
tz="UTC"
)
# Limpiamos duplicados
combined_hashtags = combined_hashtags[!duplicated(combined_hashtags),]
combined_hashtags[!is.na(combined_hashtags['created_at_R_date']),]
hashtags_counts_by_date = as.data.frame(
combined_hashtags %>%
group_by(created_at_R_date, hashtags) %>%
count(hashtags)
)
names(hashtags_counts_by_date)[1] = 'date'
names(hashtags_counts_by_date)[3] = 'count'
# Elimino todos los tweets sin hashtags (NA)
hashtags_counts_by_date = hashtags_counts_by_date[!is.na(hashtags_counts_by_date['hashtags']),]
# Elimino ahora valores de hashtags que considero genéricos o de clasificación
# Estos hashtags no agregan valor al contenido
filtered_hashtag_counts_by_date = as.data.frame(
hashtags_counts_by_date %>%
filter(
!str_detect(str_to_lower(hashtags), str_to_lower(".*COVID.*|.*coronavirus.*"))
)
)
# From summary
hitos_min_date=as.POSIXct("2020-04-25")
hitos_max_date=as.POSIXct("2020-05-15")
filtered_hashtag_counts_by_date_range = as.data.frame(
filtered_hashtag_counts_by_date %>%
filter(date >= hitos_min_date & date <= hitos_max_date)
)
# Consigo el hashtag mas usado para un determinado día
top_hashtags_by_news_date = as.data.frame(
filtered_hashtag_counts_by_date_range %>%
group_by(date) %>%
top_n(1, count)
)
# Me quedo con los que tienen valores significativos (> 3)
top_hashtags_by_news_date = top_hashtags_by_news_date[top_hashtags_by_news_date["count"]>3,]
Graficamos los hashtags mas usados por día (evaluando contenido)
p2 <- top_hashtags_by_news_date %>%
ggplot( aes(x=date, y=count, size=count, color=hashtags)) +
geom_point() +
ggtitle("most used hashtag by date") +
xlab("date") +
ylab("times used") +
theme_bw()
ggplotly(p2)
Algunas fechas Y hashtags que parecen importantes
top_hashtags_by_news_date %>%
arrange(hashtags) %>% # First sort by val. This sort the dataframe but NOT the factor levels
mutate(name=factor(hashtags, levels=hashtags)) %>% # This trick update the factor levels
ggplot( aes(x=hashtags, y=count)) +
geom_segment( aes(xend=hashtags, yend=0)) +
geom_point( size=4, color="orange") +
coord_flip() +
theme_bw() +
xlab("")
Error in `levels<-`(`*tmp*`, value = as.character(levels)) :
factor level [7] is duplicated
LS0tCnRpdGxlOiAiRE1VQkEgVFAwMSIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKQ2FyZ2EgaW5pY2lhbCBkZWwgZGF0YWZyYW1lIGRlIHR3ZWV0cyBDT1ZJRC0xOQpgYGB7cn0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkobW9uZ29saXRlKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkocGxvdGx5KQpsaWJyYXJ5KGhyYnJ0aGVtZXMpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoaGVyZSkKCnR3ZWV0c19tb25nb19jb3ZpZDE5IDwtIG1vbmdvKAogIGNvbGxlY3Rpb24gPSAidHdlZXRzX21vbmdvX2NvdmlkMTkiLCAKICBkYiA9ICJETVVCQSIKKQoKaGl0b3NfZGYgPC0gcmVhZC5jc3YoCiAgaGVyZSgicmVzb3VyY2VzIiAsICJoaXRvc19kaXNjcmV0aXphZG8uY3N2IikKKQpgYGAKCk1vbmdvIFF1ZXJ5CmBgYHtyfQpkZl90d2VldHMgPSB0d2VldHNfbW9uZ29fY292aWQxOSRhZ2dyZWdhdGUoCidbCiAgewogICAgICAiJG1hdGNoIjoge30KICB9LAogIHsKICAgICAgIiRwcm9qZWN0IjogewogICAgICAKICAgICAgInN0YXR1c19pZCI6IDEsCiAgICAgICJ1c2VyX2lkIjogMSwKICAgICAgInNjcmVlbl9uYW1lIjogMSwKICAgICAgInZlcmlmaWVkIjogMSwKICAgICAgImxvY2F0aW9uIjogMSwKICAgICAgInNvdXJjZSI6IDEsCiAgICAgICJmYXZvcml0ZV9jb3VudCI6IDEsCiAgICAgICJyZXR3ZWV0X2NvdW50IjogMSwKICAgICAgImNyZWF0ZWRfYXQiOiB7CiAgICAgICAgIiRkYXRlVG9TdHJpbmciOiB7ICJkYXRlIjogIiRjcmVhdGVkX2F0In0KICAgICAgfSwKCiAgICAgICJyZXR3ZWV0X3N0YXR1c19pZCI6IDEsICAKICAgICAgInJldHdlZXRfdXNlcl9pZCI6IDEsCiAgICAgICJyZXR3ZWV0X3NjcmVlbl9uYW1lIjogMSwKICAgICAgInJldHdlZXRfdmVyaWZpZWQiOiAxLAogICAgICAicmV0d2VldF9sb2NhdGlvbiI6IDEsCiAgICAgICJyZXR3ZWV0X3NvdXJjZSI6IDEsCiAgICAgICJyZXR3ZWV0X2Zhdm9yaXRlX2NvdW50IjogMSwKICAgICAgInJldHdlZXRfcmV0d2VldF9jb3VudCI6IDEsCiAgICAgICJyZXR3ZWV0X2NyZWF0ZWRfYXQiOiB7CiAgICAgICAgIiRjb25kIjogeyAKICAgICAgICAgICJpZiI6IHsgCiAgICAgICAgICAgICIkZXEiIDogWyIkcmV0d2VldF9jcmVhdGVkX2F0Iiwge31dIAogICAgICAgICAgfSwgCiAgICAgICAgICAidGhlbiI6IG51bGwsIAogICAgICAgICAgImVsc2UiOiB7CiAgICAgICAgICAgICIkZGF0ZVRvU3RyaW5nIjogeyJkYXRlIjogIiRyZXR3ZWV0X2NyZWF0ZWRfYXQifQogICAgICAgICAgfQogICAgICAgIH0KICAgICAgfSwKICAgICAgICAgICAgCiAgICAgICJxdW90ZWRfc3RhdHVzX2lkIjogMSwgIAogICAgICAicXVvdGVkX3VzZXJfaWQiOiAxLAogICAgICAicXVvdGVkX3NjcmVlbl9uYW1lIjogMSwKICAgICAgInF1b3RlZF92ZXJpZmllZCI6IDEsCiAgICAgICJxdW90ZWRfbG9jYXRpb24iOiAxLAogICAgICAicXVvdGVkX3NvdXJjZSI6IDEsCiAgICAgICJxdW90ZWRfZmF2b3JpdGVfY291bnQiOiAxLAogICAgICAicXVvdGVkX3JldHdlZXRfY291bnQiOiAxLAogICAgICAicXVvdGVkX2NyZWF0ZWRfYXQiOiB7CiAgICAgICAgIiRjb25kIjogeyAKICAgICAgICAgICJpZiI6IHsgCiAgICAgICAgICAgICIkZXEiIDogWyIkcXVvdGVkX2NyZWF0ZWRfYXQiLCB7fV0gCiAgICAgICAgICB9LCAKICAgICAgICAgICJ0aGVuIjogbnVsbCwgCiAgICAgICAgICAiZWxzZSI6IHsKICAgICAgICAgICAgIiRkYXRlVG9TdHJpbmciOiB7ImRhdGUiOiAiJHF1b3RlZF9jcmVhdGVkX2F0In0KICAgICAgICAgIH0KICAgICAgICB9CiAgICAgIH0KICAgIH0KICB9Cl0nCikKYGBgCgpVbmlmaWNhbW9zIHR3ZWV0cyBvcmlnaW5hbGVzLCByZXR3ZWV0cyB5IHF1b3RlcyBiYWpvIGF0cmlidXRvcyBjb211bmVzLgooCiAgJ3N0YXR1c19pZCcsCiAgJ3VzZXJfaWQnLAogICdzY3JlZW5fbmFtZScsCiAgJ3ZlcmlmaWVkJywKICAnbG9jYXRpb24nLAogICdzb3VyY2UnLAogICdmYXZvcml0ZV9jb3VudCcsCiAgJ3JldHdlZXRfY291bnQnLAogICdjcmVhdGVkX2F0JwopCmBgYHtyfQojIE9yaWdpbmFsIFR3ZWV0cwpvcmlnaW5hbF90d2VldHNfaGVhZGVyIDwtIGMoCiAgJ3N0YXR1c19pZCcsCiAgJ3VzZXJfaWQnLAogICdzY3JlZW5fbmFtZScsCiAgJ3ZlcmlmaWVkJywKICAnbG9jYXRpb24nLAogICdzb3VyY2UnLAogICdmYXZvcml0ZV9jb3VudCcsCiAgJ3JldHdlZXRfY291bnQnLAogICdjcmVhdGVkX2F0JwopCm9yaWdpbmFsX3R3ZWV0cyA9IGRmX3R3ZWV0c1ssb3JpZ2luYWxfdHdlZXRzX2hlYWRlcl0KCiMgUmV0d2VldHMKcmV0d2VldGVkX3R3ZWV0c19oZWFkZXIgPC0gYygKICAncmV0d2VldF9zdGF0dXNfaWQnLAogICdyZXR3ZWV0X3VzZXJfaWQnLAogICdyZXR3ZWV0X3NjcmVlbl9uYW1lJywKICAncmV0d2VldF92ZXJpZmllZCcsCiAgJ3JldHdlZXRfbG9jYXRpb24nLAogICdyZXR3ZWV0X3NvdXJjZScsCiAgJ3JldHdlZXRfZmF2b3JpdGVfY291bnQnLAogICdyZXR3ZWV0X3JldHdlZXRfY291bnQnLAogICdyZXR3ZWV0X2NyZWF0ZWRfYXQnCikKCnJldHdlZXRlZF90d2VldHMgPSBkZl90d2VldHNbLHJldHdlZXRlZF90d2VldHNfaGVhZGVyXQoKbmFtZXMocmV0d2VldGVkX3R3ZWV0cylbbmFtZXMocmV0d2VldGVkX3R3ZWV0cykgPT0gJ3JldHdlZXRfc3RhdHVzX2lkJ10gPC0gJ3N0YXR1c19pZCcKbmFtZXMocmV0d2VldGVkX3R3ZWV0cylbbmFtZXMocmV0d2VldGVkX3R3ZWV0cykgPT0gJ3JldHdlZXRfdXNlcl9pZCddIDwtICd1c2VyX2lkJwpuYW1lcyhyZXR3ZWV0ZWRfdHdlZXRzKVtuYW1lcyhyZXR3ZWV0ZWRfdHdlZXRzKSA9PSAncmV0d2VldF9zY3JlZW5fbmFtZSddIDwtICdzY3JlZW5fbmFtZScKbmFtZXMocmV0d2VldGVkX3R3ZWV0cylbbmFtZXMocmV0d2VldGVkX3R3ZWV0cykgPT0gJ3JldHdlZXRfdmVyaWZpZWQnXSA8LSAndmVyaWZpZWQnCm5hbWVzKHJldHdlZXRlZF90d2VldHMpW25hbWVzKHJldHdlZXRlZF90d2VldHMpID09ICdyZXR3ZWV0X2xvY2F0aW9uJ10gPC0gJ2xvY2F0aW9uJwpuYW1lcyhyZXR3ZWV0ZWRfdHdlZXRzKVtuYW1lcyhyZXR3ZWV0ZWRfdHdlZXRzKSA9PSAncmV0d2VldF9zb3VyY2UnXSA8LSAnc291cmNlJwpuYW1lcyhyZXR3ZWV0ZWRfdHdlZXRzKVtuYW1lcyhyZXR3ZWV0ZWRfdHdlZXRzKSA9PSAncmV0d2VldF9mYXZvcml0ZV9jb3VudCddIDwtICdmYXZvcml0ZV9jb3VudCcKbmFtZXMocmV0d2VldGVkX3R3ZWV0cylbbmFtZXMocmV0d2VldGVkX3R3ZWV0cykgPT0gJ3JldHdlZXRfcmV0d2VldF9jb3VudCddIDwtICdyZXR3ZWV0X2NvdW50JwpuYW1lcyhyZXR3ZWV0ZWRfdHdlZXRzKVtuYW1lcyhyZXR3ZWV0ZWRfdHdlZXRzKSA9PSAncmV0d2VldF9jcmVhdGVkX2F0J10gPC0gJ2NyZWF0ZWRfYXQnCgojIFF1b3RlcwpxdW90ZWRfdHdlZXRzX2hlYWRlciA8LSBjKAogICdxdW90ZWRfc3RhdHVzX2lkJywKICAncXVvdGVkX3VzZXJfaWQnLAogICdxdW90ZWRfc2NyZWVuX25hbWUnLAogICdxdW90ZWRfdmVyaWZpZWQnLAogICdxdW90ZWRfbG9jYXRpb24nLAogICdxdW90ZWRfc291cmNlJywKICAncXVvdGVkX2Zhdm9yaXRlX2NvdW50JywKICAncXVvdGVkX3JldHdlZXRfY291bnQnLAogICdxdW90ZWRfY3JlYXRlZF9hdCcKKQoKcXVvdGVkX3R3ZWV0cyA9IGRmX3R3ZWV0c1sscXVvdGVkX3R3ZWV0c19oZWFkZXJdCgpuYW1lcyhxdW90ZWRfdHdlZXRzKVtuYW1lcyhxdW90ZWRfdHdlZXRzKSA9PSAncXVvdGVkX3N0YXR1c19pZCddIDwtICdzdGF0dXNfaWQnCm5hbWVzKHF1b3RlZF90d2VldHMpW25hbWVzKHF1b3RlZF90d2VldHMpID09ICdxdW90ZWRfdXNlcl9pZCddIDwtICd1c2VyX2lkJwpuYW1lcyhxdW90ZWRfdHdlZXRzKVtuYW1lcyhxdW90ZWRfdHdlZXRzKSA9PSAncXVvdGVkX3NjcmVlbl9uYW1lJ10gPC0gJ3NjcmVlbl9uYW1lJwpuYW1lcyhxdW90ZWRfdHdlZXRzKVtuYW1lcyhxdW90ZWRfdHdlZXRzKSA9PSAncXVvdGVkX3ZlcmlmaWVkJ10gPC0gJ3ZlcmlmaWVkJwpuYW1lcyhxdW90ZWRfdHdlZXRzKVtuYW1lcyhxdW90ZWRfdHdlZXRzKSA9PSAncXVvdGVkX2xvY2F0aW9uJ10gPC0gJ2xvY2F0aW9uJwpuYW1lcyhxdW90ZWRfdHdlZXRzKVtuYW1lcyhxdW90ZWRfdHdlZXRzKSA9PSAncXVvdGVkX3NvdXJjZSddIDwtICdzb3VyY2UnCm5hbWVzKHF1b3RlZF90d2VldHMpW25hbWVzKHF1b3RlZF90d2VldHMpID09ICdxdW90ZWRfZmF2b3JpdGVfY291bnQnXSA8LSAnZmF2b3JpdGVfY291bnQnCm5hbWVzKHF1b3RlZF90d2VldHMpW25hbWVzKHF1b3RlZF90d2VldHMpID09ICdxdW90ZWRfcmV0d2VldF9jb3VudCddIDwtICdyZXR3ZWV0X2NvdW50JwpuYW1lcyhxdW90ZWRfdHdlZXRzKVtuYW1lcyhxdW90ZWRfdHdlZXRzKSA9PSAncXVvdGVkX2NyZWF0ZWRfYXQnXSA8LSAnY3JlYXRlZF9hdCcKYGBgCgpDb21iaW5hbW9zIGxvcyB0d2VldHMgeSBhcGxpY2Ftb3MgZm9ybWF0ZW8gYSBsb3MgdmFsb3JlcyBkZSBmZWNoYQpgYGB7cn0KY29tYmluZWRfdHdlZXRzID0gcmJpbmQob3JpZ2luYWxfdHdlZXRzLCByZXR3ZWV0ZWRfdHdlZXRzLCBxdW90ZWRfdHdlZXRzKQoKY29tYmluZWRfdHdlZXRzWydjcmVhdGVkX2F0X1JfZGF0ZSddID0gYXMuUE9TSVhjdCgKICBjb21iaW5lZF90d2VldHMkY3JlYXRlZF9hdCwgCiAgZm9ybWF0PSIlWS0lbS0lZFQiLCAKICB0ej0iVVRDIgopCmNvbWJpbmVkX3R3ZWV0c1snY3JlYXRlZF9hdCddID0gYXMuUE9TSVhjdCgKICBjb21iaW5lZF90d2VldHMkY3JlYXRlZF9hdCwgCiAgZm9ybWF0PSIlWS0lbS0lZFQlSDolTTolUyIsIAogIHR6PSJVVEMiCikKIyBMaW1waWFtb3MgZHVwbGljYWRvcwpjb21iaW5lZF90d2VldHMgPSBjb21iaW5lZF90d2VldHNbIWR1cGxpY2F0ZWQoY29tYmluZWRfdHdlZXRzKSxdCmNvbWJpbmVkX3R3ZWV0c1shaXMubmEoY29tYmluZWRfdHdlZXRzWydjcmVhdGVkX2F0X1JfZGF0ZSddKSxdCgpgYGAKClRyYW5zZm9ybWFtb3MgbnVlc3RybyBkYXRhc2V0IGRlIGhpdG9zCmBgYHtyfQpoaXRvc19kZlsnRmVjaGEnXSA9IGFzLkRhdGUoaGl0b3NfZGYkRmVjaGEsICIlZC8lbS8lWSIpCmhpdG9zX2RmWydQcmltZXJhJ10gPC0gaWZlbHNlKGhpdG9zX2RmWydQcmltZXJhJ10gPT0gIlMiLCAxLCAwKSAKYGBgCgoKYGBge3J9CnR3ZWV0X2NvdW50c19ieV9kYXRlID0gYXMuZGF0YS5mcmFtZSgKICBjb21iaW5lZF90d2VldHMgJT4lCiAgZ3JvdXBfYnkoY3JlYXRlZF9hdF9SX2RhdGUpICU+JQogIGNvdW50KGNyZWF0ZWRfYXRfUl9kYXRlKQopCm5hbWVzKHR3ZWV0X2NvdW50c19ieV9kYXRlKVsxXSA9ICdkYXRlJwpuYW1lcyh0d2VldF9jb3VudHNfYnlfZGF0ZSlbMl0gPSAnY291bnQnCiAgCnN1bW1hcnkoaGl0b3NfZGYpCmBgYAoKYGBge3J9CiMgRnJvbSBzdW1tYXJ5CmhpdG9zX21pbl9kYXRlPWFzLlBPU0lYY3QoIjIwMjAtMDQtMjUiKQpoaXRvc19tYXhfZGF0ZT1hcy5QT1NJWGN0KCIyMDIwLTA1LTE1IikKCnR3ZWV0X2NvdW50c19ieV9kYXRlX3JhbmdlID0gYXMuZGF0YS5mcmFtZSgKICB0d2VldF9jb3VudHNfYnlfZGF0ZSAlPiUKICAgIGZpbHRlcihkYXRlID49IGhpdG9zX21pbl9kYXRlICYgZGF0ZSA8PSBoaXRvc19tYXhfZGF0ZSkKKQoKc3VtbWFyeSh0d2VldF9jb3VudHNfYnlfZGF0ZV9yYW5nZSkKYGBgCgpHcmFmaWNhbW9zIGxhIGNhbnRpZGFkIGRlIHR3ZWV0cyBwb3IgZMOtYQpgYGB7cn0KIyBVc3VhbCBhcmVhIGNoYXJ0CnAgPC0gdHdlZXRfY291bnRzX2J5X2RhdGVfcmFuZ2UgJT4lCiAgZ2dwbG90KCBhZXMoeD1kYXRlLCB5PWNvdW50KSkgKwogICAgZ2VvbV9hcmVhKGZpbGw9IiM2OWIzYTIiLCBhbHBoYT0wLjUpICsKICAgIGdlb21fbGluZShjb2xvcj0iIzY5YjNhMiIpICsKICAgIHlsYWIoInR3ZWV0cyBieSBuZXdzIGV2ZW50cyBkYXRlcyIpICsKICAgIHRoZW1lX2lwc3VtKCkKCiMgVHVybiBpdCBpbnRlcmFjdGl2ZSB3aXRoIGdncGxvdGx5CnAgPC0gZ2dwbG90bHkocCkKcApgYGAKCgojIEV4cGxvcmFtb3MgZWwgdXNvIGRlIGhhc2h0YWdzCllhIHRlbmllbmRvIHVuYSBwcmltZXJhIGltcHJlc2nDs24gZGUgbGEgZXZvbHVjacOzbiBkZSBsb3MgdHdlZXRzIGVuIGJhc2Ugc3VzIGZlY2hhcywgZXhwbG9yYW1vcyBlbCB1c28gZGUgaGFzaHRhZ3MKYGBge3J9CmV4cGFuZGVkX2hhc2h0YWdzID0gdHdlZXRzX21vbmdvX2NvdmlkMTkkYWdncmVnYXRlKAonWwogICAgewogICAgICAgICIkdW53aW5kIjogIiRoYXNodGFncyIKICAgIH0sCiAgICB7CiAgICAgICAgIiRwcm9qZWN0IjogewogICAgICAgICAgICAic3RhdHVzX2lkIjogMSwKICAgICAgICAgICAgInZlcmlmaWVkIjogMSwKICAgICAgICAgICAgImxvY2F0aW9uIjogMSwKICAgICAgICAgICAgInNvdXJjZSI6IDEsCiAgICAgICAgICAgICJjcmVhdGVkX2F0IjogewogICAgICAgICAgICAgICAgIiRkYXRlVG9TdHJpbmciOiB7ICJkYXRlIjogIiRjcmVhdGVkX2F0In0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgImZhdm9yaXRlX2NvdW50IjogMSwKICAgICAgICAgICAgInJldHdlZXRfY291bnQiOiAxLAoKICAgICAgICAgICAgInJldHdlZXRfc3RhdHVzX2lkIjogMSwKICAgICAgICAgICAgInJldHdlZXRfdmVyaWZpZWQiOiAxLAogICAgICAgICAgICAicmV0d2VldF9sb2NhdGlvbiI6MSwKICAgICAgICAgICAgInJldHdlZXRfc291cmNlIjogMSwKICAgICAgICAgICAgInJldHdlZXRfY3JlYXRlZF9hdCI6IHsKICAgICAgICAgICAgICAgICIkY29uZCI6IHsgCiAgICAgICAgICAgICAgICAgICAgImlmIjogeyAKICAgICAgICAgICAgICAgICAgICAgICAgIiRlcSIgOiBbIiRyZXR3ZWV0X2NyZWF0ZWRfYXQiLCB7fV0gCiAgICAgICAgICAgICAgICAgICAgfSwgCiAgICAgICAgICAgICAgICAgICAgInRoZW4iOiBudWxsLCAKICAgICAgICAgICAgICAgICAgICAiZWxzZSI6IHsKICAgICAgICAgICAgICAgICAgICAgICAgIiRkYXRlVG9TdHJpbmciOiB7ImRhdGUiOiAiJHJldHdlZXRfY3JlYXRlZF9hdCJ9CiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAicmV0d2VldF9mYXZvcml0ZV9jb3VudCI6IDEsCiAgICAgICAgICAgICJyZXR3ZWV0X3JldHdlZXRfY291bnQiOiAxLAoKICAgICAgICAgICAgInF1b3RlZF9zdGF0dXNfaWQiOiAxLAogICAgICAgICAgICAicXVvdGVkX3ZlcmlmaWVkIjogMSwKICAgICAgICAgICAgInF1b3RlZF9sb2NhdGlvbiI6IDEsCiAgICAgICAgICAgICJxdW90ZWRfc291cmNlIjogMSwgCiAgICAgICAgICAgICAicXVvdGVkX2NyZWF0ZWRfYXQiOiB7CiAgICAgICAgICAgICAgICAiJGNvbmQiOiB7IAogICAgICAgICAgICAgICAgICAgICJpZiI6IHsgCiAgICAgICAgICAgICAgICAgICAgICAgICIkZXEiIDogWyIkcXVvdGVkX2NyZWF0ZWRfYXQiLCB7fV0gCiAgICAgICAgICAgICAgICAgICAgfSwgCiAgICAgICAgICAgICAgICAgICAgInRoZW4iOiBudWxsLCAKICAgICAgICAgICAgICAgICAgICAiZWxzZSI6IHsKICAgICAgICAgICAgICAgICAgICAgICAgIiRkYXRlVG9TdHJpbmciOiB7ImRhdGUiOiAiJHF1b3RlZF9jcmVhdGVkX2F0In0KICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJxdW90ZWRfZmF2b3JpdGVfY291bnQiOiAxLAogICAgICAgICAgICAicXVvdGVkX3JldHdlZXRfY291bnQiOiAxLAoKICAgICAgICAgICAgImhhc2h0YWdzIjogMQogICAgICAgIH0KICAgIH0KXScKKQpgYGAKCmBgYHtyfQpvcmlnaW5hbF90d2VldF9oZWFkZXJzIDwtIGMoCiAgJ3N0YXR1c19pZCcsCiAgJ3ZlcmlmaWVkJywKICAnbG9jYXRpb24nLAogICdzb3VyY2UnLAogICdjcmVhdGVkX2F0JywKICAnZmF2b3JpdGVfY291bnQnLAogICdyZXR3ZWV0X2NvdW50JywKICAnaGFzaHRhZ3MnCikKb3JpZ2luYWxfdHdlZXRfaGFzaHRhZ3MgPSBleHBhbmRlZF9oYXNodGFnc1ssb3JpZ2luYWxfdHdlZXRfaGVhZGVyc10KCiMgUmV0d2VldHMKcmV0d2VldGVkX3R3ZWV0X2hlYWRlcnMgPC0gYygKICAncmV0d2VldF9zdGF0dXNfaWQnLAogICdyZXR3ZWV0X3ZlcmlmaWVkJywKICAncmV0d2VldF9sb2NhdGlvbicsCiAgJ3JldHdlZXRfc291cmNlJywKICAncmV0d2VldF9jcmVhdGVkX2F0JywKICAncmV0d2VldF9mYXZvcml0ZV9jb3VudCcsCiAgJ3JldHdlZXRfcmV0d2VldF9jb3VudCcsCiAgJ2hhc2h0YWdzJwopCgpyZXR3ZWV0ZWRfdHdlZXRfaGFzaHRhZ3MgPSBleHBhbmRlZF9oYXNodGFnc1sscmV0d2VldGVkX3R3ZWV0X2hlYWRlcnNdCgpuYW1lcyhyZXR3ZWV0ZWRfdHdlZXRfaGFzaHRhZ3MpW25hbWVzKHJldHdlZXRlZF90d2VldF9oYXNodGFncykgPT0gJ3JldHdlZXRfc3RhdHVzX2lkJ10gPC0gJ3N0YXR1c19pZCcKbmFtZXMocmV0d2VldGVkX3R3ZWV0X2hhc2h0YWdzKVtuYW1lcyhyZXR3ZWV0ZWRfdHdlZXRfaGFzaHRhZ3MpID09ICdyZXR3ZWV0X3ZlcmlmaWVkJ10gPC0gJ3ZlcmlmaWVkJwpuYW1lcyhyZXR3ZWV0ZWRfdHdlZXRfaGFzaHRhZ3MpW25hbWVzKHJldHdlZXRlZF90d2VldF9oYXNodGFncykgPT0gJ3JldHdlZXRfbG9jYXRpb24nXSA8LSAnbG9jYXRpb24nCm5hbWVzKHJldHdlZXRlZF90d2VldF9oYXNodGFncylbbmFtZXMocmV0d2VldGVkX3R3ZWV0X2hhc2h0YWdzKSA9PSAncmV0d2VldF9zb3VyY2UnXSA8LSAnc291cmNlJwpuYW1lcyhyZXR3ZWV0ZWRfdHdlZXRfaGFzaHRhZ3MpW25hbWVzKHJldHdlZXRlZF90d2VldF9oYXNodGFncykgPT0gJ3JldHdlZXRfY3JlYXRlZF9hdCddIDwtICdjcmVhdGVkX2F0JwpuYW1lcyhyZXR3ZWV0ZWRfdHdlZXRfaGFzaHRhZ3MpW25hbWVzKHJldHdlZXRlZF90d2VldF9oYXNodGFncykgPT0gJ3JldHdlZXRfZmF2b3JpdGVfY291bnQnXSA8LSAnZmF2b3JpdGVfY291bnQnCm5hbWVzKHJldHdlZXRlZF90d2VldF9oYXNodGFncylbbmFtZXMocmV0d2VldGVkX3R3ZWV0X2hhc2h0YWdzKSA9PSAncmV0d2VldF9yZXR3ZWV0X2NvdW50J10gPC0gJ3JldHdlZXRfY291bnQnCgojIFF1b3RlcwpxdW90ZWRfdHdlZXRfaGVhZGVycyA8LSBjKAogICdxdW90ZWRfc3RhdHVzX2lkJywKICAncXVvdGVkX3ZlcmlmaWVkJywKICAncXVvdGVkX2xvY2F0aW9uJywKICAncXVvdGVkX3NvdXJjZScsCiAgJ3F1b3RlZF9jcmVhdGVkX2F0JywKICAncXVvdGVkX2Zhdm9yaXRlX2NvdW50JywKICAncXVvdGVkX3JldHdlZXRfY291bnQnLAogICdoYXNodGFncycKKQpxdW90ZWRfdHdlZXRfaGFzaHRhZ3MgPSBleHBhbmRlZF9oYXNodGFnc1sscXVvdGVkX3R3ZWV0X2hlYWRlcnNdCgpuYW1lcyhxdW90ZWRfdHdlZXRfaGFzaHRhZ3MpW25hbWVzKHF1b3RlZF90d2VldF9oYXNodGFncykgPT0gJ3F1b3RlZF9zdGF0dXNfaWQnXSA8LSAnc3RhdHVzX2lkJwpuYW1lcyhxdW90ZWRfdHdlZXRfaGFzaHRhZ3MpW25hbWVzKHF1b3RlZF90d2VldF9oYXNodGFncykgPT0gJ3F1b3RlZF92ZXJpZmllZCddIDwtICd2ZXJpZmllZCcKbmFtZXMocXVvdGVkX3R3ZWV0X2hhc2h0YWdzKVtuYW1lcyhxdW90ZWRfdHdlZXRfaGFzaHRhZ3MpID09ICdxdW90ZWRfbG9jYXRpb24nXSA8LSAnbG9jYXRpb24nCm5hbWVzKHF1b3RlZF90d2VldF9oYXNodGFncylbbmFtZXMocXVvdGVkX3R3ZWV0X2hhc2h0YWdzKSA9PSAncXVvdGVkX3NvdXJjZSddIDwtICdzb3VyY2UnCm5hbWVzKHF1b3RlZF90d2VldF9oYXNodGFncylbbmFtZXMocXVvdGVkX3R3ZWV0X2hhc2h0YWdzKSA9PSAncXVvdGVkX2NyZWF0ZWRfYXQnXSA8LSAnY3JlYXRlZF9hdCcKbmFtZXMocXVvdGVkX3R3ZWV0X2hhc2h0YWdzKVtuYW1lcyhxdW90ZWRfdHdlZXRfaGFzaHRhZ3MpID09ICdxdW90ZWRfZmF2b3JpdGVfY291bnQnXSA8LSAnZmF2b3JpdGVfY291bnQnCm5hbWVzKHF1b3RlZF90d2VldF9oYXNodGFncylbbmFtZXMocXVvdGVkX3R3ZWV0X2hhc2h0YWdzKSA9PSAncXVvdGVkX3JldHdlZXRfY291bnQnXSA8LSAncmV0d2VldF9jb3VudCcKYGBgCgpgYGB7cn0KY29tYmluZWRfaGFzaHRhZ3MgPSByYmluZChvcmlnaW5hbF90d2VldF9oYXNodGFncywgcmV0d2VldGVkX3R3ZWV0X2hhc2h0YWdzLCBxdW90ZWRfdHdlZXRfaGFzaHRhZ3MpCgpjb21iaW5lZF9oYXNodGFnc1snY3JlYXRlZF9hdF9SX2RhdGUnXSA9IGFzLlBPU0lYY3QoCiAgY29tYmluZWRfaGFzaHRhZ3MkY3JlYXRlZF9hdCwgCiAgZm9ybWF0PSIlWS0lbS0lZFQiLCAKICB0ej0iVVRDIgopCmNvbWJpbmVkX2hhc2h0YWdzWydjcmVhdGVkX2F0J10gPSBhcy5QT1NJWGN0KAogIGNvbWJpbmVkX2hhc2h0YWdzJGNyZWF0ZWRfYXQsIAogIGZvcm1hdD0iJVktJW0tJWRUJUg6JU06JVMiLCAKICB0ej0iVVRDIgopCgojIExpbXBpYW1vcyBkdXBsaWNhZG9zCmNvbWJpbmVkX2hhc2h0YWdzID0gY29tYmluZWRfaGFzaHRhZ3NbIWR1cGxpY2F0ZWQoY29tYmluZWRfaGFzaHRhZ3MpLF0KY29tYmluZWRfaGFzaHRhZ3NbIWlzLm5hKGNvbWJpbmVkX2hhc2h0YWdzWydjcmVhdGVkX2F0X1JfZGF0ZSddKSxdCmBgYAoKCmBgYHtyfQpoYXNodGFnc19jb3VudHNfYnlfZGF0ZSA9IGFzLmRhdGEuZnJhbWUoCiAgY29tYmluZWRfaGFzaHRhZ3MgJT4lCiAgZ3JvdXBfYnkoY3JlYXRlZF9hdF9SX2RhdGUsIGhhc2h0YWdzKSAlPiUKICBjb3VudChoYXNodGFncykKKQoKbmFtZXMoaGFzaHRhZ3NfY291bnRzX2J5X2RhdGUpWzFdID0gJ2RhdGUnCm5hbWVzKGhhc2h0YWdzX2NvdW50c19ieV9kYXRlKVszXSA9ICdjb3VudCcKCiMgRWxpbWlubyB0b2RvcyBsb3MgdHdlZXRzIHNpbiBoYXNodGFncyAoTkEpCmhhc2h0YWdzX2NvdW50c19ieV9kYXRlID0gaGFzaHRhZ3NfY291bnRzX2J5X2RhdGVbIWlzLm5hKGhhc2h0YWdzX2NvdW50c19ieV9kYXRlWydoYXNodGFncyddKSxdCgpgYGAKCmBgYHtyfQojIEVsaW1pbm8gYWhvcmEgdmFsb3JlcyBkZSBoYXNodGFncyBxdWUgY29uc2lkZXJvIGdlbsOpcmljb3MgbyBkZSBjbGFzaWZpY2FjacOzbiAKIyBFc3RvcyBoYXNodGFncyBubyBhZ3JlZ2FuIHZhbG9yIGFsIGNvbnRlbmlkbwpmaWx0ZXJlZF9oYXNodGFnX2NvdW50c19ieV9kYXRlID0gYXMuZGF0YS5mcmFtZSgKICBoYXNodGFnc19jb3VudHNfYnlfZGF0ZSAlPiUKICBmaWx0ZXIoCiAgICAhc3RyX2RldGVjdChzdHJfdG9fbG93ZXIoaGFzaHRhZ3MpLCBzdHJfdG9fbG93ZXIoIi4qQ09WSUQuKnwuKmNvcm9uYXZpcnVzLioiKSkKICApCikKCiMgRnJvbSBzdW1tYXJ5CmhpdG9zX21pbl9kYXRlPWFzLlBPU0lYY3QoIjIwMjAtMDQtMjUiKQpoaXRvc19tYXhfZGF0ZT1hcy5QT1NJWGN0KCIyMDIwLTA1LTE1IikKCmZpbHRlcmVkX2hhc2h0YWdfY291bnRzX2J5X2RhdGVfcmFuZ2UgPSBhcy5kYXRhLmZyYW1lKAogIGZpbHRlcmVkX2hhc2h0YWdfY291bnRzX2J5X2RhdGUgJT4lCiAgICBmaWx0ZXIoZGF0ZSA+PSBoaXRvc19taW5fZGF0ZSAmIGRhdGUgPD0gaGl0b3NfbWF4X2RhdGUpCikKCiMgQ29uc2lnbyBlbCBoYXNodGFnIG1hcyB1c2FkbyBwYXJhIHVuIGRldGVybWluYWRvIGTDrWEKdG9wX2hhc2h0YWdzX2J5X25ld3NfZGF0ZSA9IGFzLmRhdGEuZnJhbWUoCiAgZmlsdGVyZWRfaGFzaHRhZ19jb3VudHNfYnlfZGF0ZV9yYW5nZSAlPiUgCiAgZ3JvdXBfYnkoZGF0ZSkgJT4lIAogIHRvcF9uKDEsIGNvdW50KQopCgojIE1lIHF1ZWRvIGNvbiBsb3MgcXVlIHRpZW5lbiB2YWxvcmVzIHNpZ25pZmljYXRpdm9zICg+IDMpCnRvcF9oYXNodGFnc19ieV9uZXdzX2RhdGUgPSB0b3BfaGFzaHRhZ3NfYnlfbmV3c19kYXRlW3RvcF9oYXNodGFnc19ieV9uZXdzX2RhdGVbImNvdW50Il0+MyxdCmBgYAoKR3JhZmljYW1vcyBsb3MgaGFzaHRhZ3MgbWFzIHVzYWRvcyBwb3IgZMOtYSAoZXZhbHVhbmRvIGNvbnRlbmlkbykKYGBge3J9CgpwMiA8LSB0b3BfaGFzaHRhZ3NfYnlfbmV3c19kYXRlICU+JQogIGdncGxvdCggYWVzKHg9ZGF0ZSwgeT1jb3VudCwgc2l6ZT1jb3VudCwgY29sb3I9aGFzaHRhZ3MpKSArCiAgZ2VvbV9wb2ludCgpICsKICBnZ3RpdGxlKCJNb3N0IHVzZWQgaGFzaHRhZyBieSBkYXRlIikgKwogIHhsYWIoImRhdGUiKSAgKwogIHlsYWIoInRpbWVzIHVzZWQiKSAgKwogIHRoZW1lX2J3KCkKCmdncGxvdGx5KHAyKQpgYGAKCkFsZ3VuYXMgZmVjaGFzIFkgaGFzaHRhZ3MgcXVlIHBhcmVjZW4gaW1wb3J0YW50ZXMKYGBge3J9CiMgMjAyMC0wNS0wMiBSZWdpc3RybyB0b3AgZGUgdHdlZXRzICgjUXVlZGF0ZUVuQ2FzYSkgPz8/CgojIEhhc2h0YWcgQ1VCQSBmdWUgVE9QICgyMDIwLTA1LTAzLCAyMDIwLTA1LTE1KSAoTm90aWNpYSBtZWRpY29zIGN1YmFub3MgPz8/KQoKIyBIYXNodGFnIEVzY2FuZGFsbyAyMDIwLTA1LTA4IGNvbmRpY2UgY29uIEdlcmlhdHJpY28gUmVjb2xldGEgeSBwaWNvcyBkZSBjb250YWdpbwoKYGBgCgo=